home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectInput / Scrawl / scrawl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  31.4 KB  |  998 lines

  1. //-----------------------------------------------------------------------------
  2. // File: Scrawl.cpp
  3. //
  4. // Desc: Demonstrates an application which receives relative mouse data
  5. //       in non-exclusive mode via a dialog timer.
  6. //
  7. // Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #define STRICT
  10. #include <tchar.h>
  11. #include <windows.h>
  12. #include <windowsx.h>
  13. #include <basetsd.h>
  14. #include <dinput.h>
  15. #include "resource.h"
  16.  
  17.  
  18.  
  19.  
  20. //-----------------------------------------------------------------------------
  21. // Function prototypes 
  22. //-----------------------------------------------------------------------------
  23. HRESULT InitVariables();
  24. HWND    RegisterWindowClass( HINSTANCE hInst );
  25. LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
  26.  
  27. HRESULT OnClear( HWND hWnd );
  28. VOID    InvalidateCursorRect(HWND hWnd);
  29. VOID    OnPaint( HWND hWnd );
  30. BOOL    OnCreate( HWND hWnd, LPCREATESTRUCT lpCreateStruct );
  31. VOID    OnInitMenuPopup( HWND hWnd, HMENU hMenu, UINT item, BOOL fSystemMenu );
  32. VOID    OnKeyDown( HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags );
  33. HRESULT InitDirectInput( HWND hWnd );
  34. HRESULT SetAcquire();
  35. HRESULT FreeDirectInput();
  36. VOID    OnMouseInput( HWND hWnd );
  37. VOID    OnLeftButtonDown( HWND hWnd );
  38. VOID    OnRightButtonUp( HWND hWnd );
  39.  
  40.  
  41.  
  42.  
  43. //-----------------------------------------------------------------------------
  44. // Defines, constants, and global variables
  45. //-----------------------------------------------------------------------------
  46. #define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
  47. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  48.  
  49. #define SCRAWL_CXBITMAP             512
  50. #define SCRAWL_CYBITMAP             300
  51. #define SAMPLE_BUFFER_SIZE           16
  52. #define IDC_CLEAR               64
  53. #define IDC_ABOUT               65
  54.  
  55. struct LEFTBUTTONINFO 
  56. {
  57.     HDC   hdcWindow;
  58.     BOOL  bMoved;
  59.     DWORD dwSeqLastSeen;
  60. };
  61.  
  62.  
  63. HDC     g_hDC           = NULL; // Memory DC our picture lives in 
  64. HBITMAP g_hBitmap       = NULL; // Our picture 
  65. HBITMAP g_hbmpDeselect  = NULL; // Stock bitmap for deselecting 
  66. HCURSOR g_hCursorCross  = NULL; // cross hair
  67. int     g_cxCross;              // Width of crosshairs cursor 
  68. int     g_cyCross;              // Height of crosshairs cursor 
  69. int     g_dxCrossHot;           // Hotspot location of crosshairs 
  70. int     g_dyCrossHot;           // Hotspot location of crosshairs 
  71. BOOL    g_bShowCursor   = TRUE; // Should the cursor be shown? 
  72. int     g_x;                    // Virtual x-coordinate 
  73. int     g_y;                    // Virtual y-coordinate 
  74. int     g_dxFuzz;               // Leftover x-fuzz from scaling 
  75. int     g_dyFuzz;               // Leftover y-fuzz from scaling 
  76. int     g_iSensitivity;         // Mouse sensitivity 
  77.  
  78. LPDIRECTINPUT8          g_pDI           = NULL;         
  79. LPDIRECTINPUTDEVICE8    g_pMouse        = NULL;     
  80. HANDLE                  g_hMouseEvent   = NULL;
  81. BOOL                    g_bActive       = TRUE;
  82. BOOL                    g_bSwapMouseButtons;
  83.  
  84.  
  85.  
  86.  
  87. //-----------------------------------------------------------------------------
  88. // Name: WinMain()
  89. // Desc: Entry point for the application.  Since we use a simple dialog for 
  90. //       user interaction we don't need to pump messages.
  91. //-----------------------------------------------------------------------------
  92. int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int )
  93. {
  94.     HRESULT hr;
  95.     HWND    hWnd;
  96.     BOOL    bDone;
  97.     DWORD   dwResult;
  98.     MSG     msg;
  99.  
  100.     // Initialize global varibles
  101.     if ( FAILED( hr = InitVariables() ) )
  102.     {
  103.         MessageBox( NULL, _T("Error Initializing Variables"), 
  104.                     _T("Scrawl"),  MB_ICONERROR | MB_OK );
  105.         return TRUE;
  106.     }
  107.  
  108.     // Display the main dialog box.
  109.     hWnd = RegisterWindowClass( hInstance );
  110.     if( NULL == hWnd )
  111.     {
  112.         MessageBox( NULL, _T("Error Creating Window"), 
  113.                     _T("Scrawl"), MB_ICONERROR | MB_OK );
  114.         return TRUE;
  115.     }
  116.  
  117.     // Start message pump. Since we use notification handles, we need to use
  118.     // MsgWaitForMultipleObjects() to wait for the event or a message, 
  119.     // whichever comes first.
  120.  
  121.     bDone = FALSE;
  122.     while( !bDone ) 
  123.     {
  124.         dwResult = MsgWaitForMultipleObjects( 1, &g_hMouseEvent, 
  125.                                               FALSE, INFINITE, QS_ALLINPUT );
  126.  
  127.         switch( dwResult ) 
  128.         {
  129.             // WAIT_OBJECT_0 + 0 means that g_hevtMouse was signalled 
  130.             case WAIT_OBJECT_0 + 0:
  131.                 OnMouseInput( hWnd );
  132.                 break;
  133.  
  134.             // WAIT_OBJECT_0 + 1 means that we have messages to process 
  135.             case WAIT_OBJECT_0 + 1:
  136.                 while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 
  137.                 {
  138.                     if( msg.message == WM_QUIT ) 
  139.                     {
  140.                         // Stop loop if it's a quit message
  141.                         bDone = TRUE;
  142.                     } 
  143.                     else 
  144.                     {
  145.                         TranslateMessage( &msg );
  146.                         DispatchMessage( &msg );
  147.                     }
  148.                 }
  149.                 break;
  150.         }
  151.     }
  152.  
  153.     // Clean up
  154.     FreeDirectInput();
  155.  
  156.     // Delete bitmaps
  157.     if( g_hDC ) 
  158.     {
  159.         if( g_hbmpDeselect ) 
  160.             SelectObject( g_hDC, g_hbmpDeselect );
  161.  
  162.         DeleteDC( g_hDC );
  163.     }
  164.  
  165.     if( g_hBitmap ) 
  166.         DeleteObject( g_hBitmap );
  167.  
  168.     return TRUE;
  169. }
  170.  
  171.  
  172.  
  173.  
  174. //-----------------------------------------------------------------------------
  175. // Name: InitVariables()
  176. // Desc: Initialize global varibles 
  177. //-----------------------------------------------------------------------------
  178. HRESULT InitVariables()
  179. {
  180.     ICONINFO iconInfo;
  181.     BITMAP   bitmap;
  182.     HDC      hDC;
  183.  
  184.     // Get our crosshairs cursor and extract the the width and
  185.     // hotspot location so we can draw it manually.
  186.     g_hCursorCross = LoadCursor( NULL, IDC_CROSS );
  187.  
  188.     GetIconInfo( g_hCursorCross, &iconInfo );
  189.     GetObject( iconInfo.hbmMask, sizeof(BITMAP), &bitmap );
  190.  
  191.     // Delete un-needed handles
  192.     if( iconInfo.hbmMask)  
  193.         DeleteObject( iconInfo.hbmMask );
  194.     if( iconInfo.hbmColor) 
  195.         DeleteObject( iconInfo.hbmColor );
  196.  
  197.     // Save x-y info 
  198.     g_dxCrossHot = iconInfo.xHotspot;
  199.     g_dyCrossHot = iconInfo.yHotspot;
  200.  
  201.     g_cxCross = bitmap.bmWidth;
  202.     g_cyCross = bitmap.bmHeight;
  203.  
  204.     // create and setup our scrawl bitmap.
  205.     hDC = GetDC( NULL );
  206.     g_hDC = CreateCompatibleDC( hDC );  
  207.     ReleaseDC( NULL, hDC );
  208.     if( NULL == g_hDC ) 
  209.         return E_FAIL;
  210.  
  211.     g_hBitmap = CreateBitmap( SCRAWL_CXBITMAP, SCRAWL_CYBITMAP, 1, 1, 0 );
  212.     if( NULL == g_hBitmap ) 
  213.         return E_FAIL;
  214.  
  215.     g_hbmpDeselect = (HBITMAP)SelectObject( g_hDC, g_hBitmap );
  216.  
  217.     // Clear bitmap
  218.     OnClear( NULL );  
  219.  
  220.     return S_OK;
  221. }
  222.  
  223.  
  224.  
  225.  
  226. //-----------------------------------------------------------------------------
  227. // Name: RegisterWindowClass()
  228. // Desc: Set up the window class. 
  229. //-----------------------------------------------------------------------------
  230. HWND RegisterWindowClass( HINSTANCE hInst )
  231. {
  232.     WNDCLASS wc;
  233.     wc.hCursor        = LoadCursor( 0, IDC_ARROW );
  234.     wc.hIcon          = LoadIcon( hInst, MAKEINTRESOURCE(IDI_MAIN) );
  235.     wc.lpszMenuName   = NULL;
  236.     wc.lpszClassName  = _T("Scrawl");
  237.     wc.hbrBackground  = NULL;
  238.     wc.hInstance      = hInst;
  239.     wc.style          = 0;
  240.     wc.lpfnWndProc    = WndProc;
  241.     wc.cbClsExtra     = 0;
  242.     wc.cbWndExtra     = 0;
  243.     if( FALSE == RegisterClass(&wc) ) 
  244.         return NULL;
  245.  
  246.     RECT rc;
  247.     rc.left     = 0;
  248.     rc.top      = 0;
  249.     rc.right    = SCRAWL_CXBITMAP;
  250.     rc.bottom   = SCRAWL_CYBITMAP;
  251.     AdjustWindowRectEx( &rc, WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, 
  252.                         FALSE, WS_EX_APPWINDOW );
  253.  
  254.     HWND hWnd = CreateWindowEx( WS_EX_APPWINDOW, _T("Scrawl"), _T("Scrawl"),  
  255.                                 WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
  256.                                 CW_USEDEFAULT, CW_USEDEFAULT,
  257.                                 rc.right - rc.left, 
  258.                                 rc.bottom - rc.top, 
  259.                                 NULL, NULL, hInst, NULL );
  260.  
  261.     ShowWindow( hWnd, TRUE );
  262.  
  263.     return hWnd;
  264. }
  265.  
  266.  
  267.  
  268.  
  269. //-----------------------------------------------------------------------------
  270. // Name: WndProc()
  271. // Desc: Handles window messages
  272. //-----------------------------------------------------------------------------
  273. LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, 
  274.                           LPARAM lParam )
  275. {
  276.     LRESULT lr = 0;
  277.  
  278.     switch( msg ) 
  279.     {
  280.         // Pass these messages to user defined functions
  281.         HANDLE_MSG( hWnd, WM_CREATE,        OnCreate );
  282.         HANDLE_MSG( hWnd, WM_PAINT,         OnPaint );
  283.         HANDLE_MSG( hWnd, WM_INITMENUPOPUP, OnInitMenuPopup );
  284.         HANDLE_MSG( hWnd, WM_KEYDOWN,       OnKeyDown );
  285.  
  286.         case WM_ACTIVATE:   // sent when window changes active state
  287.             if( WA_INACTIVE == wParam )
  288.                 g_bActive = FALSE;
  289.             else
  290.                 g_bActive = TRUE;
  291.  
  292.             // Set exclusive mode access to the mouse based on active state
  293.             SetAcquire();
  294.             return 0;
  295.  
  296.         case WM_NCLBUTTONDOWN:
  297.             switch (wParam)
  298.             {
  299.                 case HTMINBUTTON:
  300.                     ShowWindow( hWnd, SW_MINIMIZE);
  301.                     break;
  302.  
  303.                 case HTCLOSE:
  304.                     PostQuitMessage(0);
  305.                     break;
  306.             }
  307.  
  308.         case WM_ENTERMENULOOP:
  309.         case WM_ENTERSIZEMOVE:
  310.             // un-acquire device when entering menu or re-sizing
  311.             // this will show the mouse cursor again
  312.             g_bActive = FALSE;
  313.             SetAcquire();
  314.             return 0;
  315.  
  316.         case WM_EXITMENULOOP:
  317.             // If we aren't returning from the popup menu, let the user continue
  318.             // to be in non-exclusive mode (to move the window for example)
  319.             if( (BOOL)wParam == FALSE )
  320.                 return 0;
  321.  
  322.         case WM_EXITSIZEMOVE:
  323.             // re-acquire device when leaving menu or re-sizing
  324.             // this will show the mouse cursor again
  325.  
  326.             // even though the menu is going away, the app
  327.             // might have lost focus or be an icon
  328.             if( GetActiveWindow() == hWnd || !IsIconic( hWnd ) )
  329.                 g_bActive = TRUE;
  330.             else
  331.                 g_bActive = FALSE;
  332.  
  333.             SetAcquire();
  334.             return 0;
  335.         
  336.         case WM_SYSCOMMAND:
  337.             lr = 0;
  338.             switch ( LOWORD(wParam) ) 
  339.             {
  340.                 case IDC_CLEAR:
  341.                     OnClear( hWnd );
  342.                     break;
  343.             
  344.                 case IDC_ABOUT:
  345.                     MessageBox( hWnd, _T("Scrawl DirectInput Sample v1.0"),
  346.                                 _T("Scrawl"), MB_OK );
  347.                     break;
  348.             
  349.                 case SC_SCREENSAVE:
  350.                     // eat the screen-saver notification.
  351.                     break;
  352.  
  353.                 case IDC_SENSITIVITY_LOW:
  354.                     g_iSensitivity = -1;
  355.                     break;
  356.  
  357.                 case IDC_SENSITIVITY_NORMAL:
  358.                     g_iSensitivity = 0;
  359.                     break;
  360.  
  361.                 case IDC_SENSITIVITY_HIGH:
  362.                     g_iSensitivity = 1;
  363.                     break;
  364.             
  365.                 default:
  366.                     lr = DefWindowProc( hWnd, msg, wParam, lParam );
  367.                     break;
  368.             }
  369.         
  370.             // The WM_SYSCOMMAND might've been a WM_CLOSE, 
  371.             // in which case our window no longer exists.  
  372.             if( IsWindow(hWnd) ) 
  373.                 SetAcquire();
  374.             return lr;
  375.  
  376.  
  377.        case WM_DESTROY:
  378.             PostQuitMessage(0);
  379.             break;
  380.     }
  381.  
  382.     return DefWindowProc( hWnd, msg, wParam, lParam );
  383. }
  384.  
  385.  
  386.  
  387.  
  388. //-----------------------------------------------------------------------------
  389. // Name: OnCreate()
  390. // Desc: Handles the WM_CREATE window message
  391. //-----------------------------------------------------------------------------
  392. BOOL OnCreate( HWND hWnd, LPCREATESTRUCT lpCreateStruct )
  393. {
  394.     HRESULT hr;
  395.     HMENU   hMenu;
  396.  
  397.     // Initialize direct input
  398.     hr = InitDirectInput( hWnd );
  399.     if( FAILED(hr) )
  400.     {
  401.         MessageBox( NULL, _T("Error Initializing DirectInput"), 
  402.                     _T("Scrawl"), MB_ICONERROR | MB_OK );
  403.         return FALSE;
  404.     }
  405.  
  406.     // Fix up the popup system menu with custom commands
  407.     hMenu = GetSystemMenu( hWnd, FALSE );
  408.  
  409.     EnableMenuItem( hMenu, SC_SIZE,     MF_BYCOMMAND | MF_DISABLED | MF_GRAYED );
  410.     EnableMenuItem( hMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED );
  411.     AppendMenu( hMenu, MF_ENABLED | MF_STRING, IDC_CLEAR, _T("C&lear\tDel") );
  412.     AppendMenu( hMenu, MF_ENABLED | MF_STRING, IDC_ABOUT, _T("&About\tF1") );
  413.  
  414.     AppendMenu( hMenu, MF_ENABLED | MF_STRING | MF_POPUP,
  415.                 (UINT_PTR)LoadMenu( (HINSTANCE)GetModuleHandle(NULL), 
  416.                                     MAKEINTRESOURCE(IDM_SENSITIVITY) ),
  417.                 _T("Sensitivit&y") );
  418.  
  419.     return TRUE;    
  420. }
  421.  
  422.  
  423.  
  424.  
  425. //-----------------------------------------------------------------------------
  426. // Name: OnPaint()
  427. // Desc: Handles the WM_PAINT window message
  428. //-----------------------------------------------------------------------------
  429. VOID OnPaint( HWND hWnd )
  430. {
  431.     PAINTSTRUCT ps;
  432.     HDC         hDC;
  433.     
  434.     hDC = BeginPaint( hWnd, &ps );
  435.     if( NULL == hDC ) 
  436.         return;
  437.  
  438.     BitBlt( hDC, ps.rcPaint.left, ps.rcPaint.top,
  439.             ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
  440.             g_hDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY );
  441.  
  442.     if( g_bActive && g_bShowCursor ) 
  443.         DrawIcon( hDC, g_x - g_dxCrossHot, g_y - g_dyCrossHot, g_hCursorCross );
  444.  
  445.     EndPaint( hWnd, &ps );
  446. }
  447.  
  448.  
  449.  
  450.  
  451. //-----------------------------------------------------------------------------
  452. // Name: OnInitMenuPopup()
  453. // Desc: Handles the WM_INITMENUPOPUP window message
  454. //-----------------------------------------------------------------------------
  455. VOID OnInitMenuPopup( HWND hWnd, HMENU hMenu, UINT item, BOOL fSystemMenu )
  456. {
  457.     for( int iSensitivity = -1; iSensitivity <= 1; iSensitivity++ ) 
  458.     {
  459.         if( g_iSensitivity == iSensitivity ) 
  460.         {
  461.             CheckMenuItem( hMenu, IDC_SENSITIVITY_NORMAL + iSensitivity,
  462.                            MF_BYCOMMAND | MF_CHECKED );
  463.         } 
  464.         else 
  465.         {
  466.             CheckMenuItem( hMenu, IDC_SENSITIVITY_NORMAL + iSensitivity,
  467.                            MF_BYCOMMAND | MF_UNCHECKED );
  468.         }
  469.     }
  470. }
  471.  
  472.  
  473.  
  474.  
  475. //-----------------------------------------------------------------------------
  476. // Name: OnKeyDown()
  477. // Desc: Handles the WM_KEYDOWN window message
  478. //-----------------------------------------------------------------------------
  479. VOID OnKeyDown( HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags )
  480. {
  481.     switch( vk ) 
  482.     {
  483.         case '1':
  484.         case '2':
  485.         case '3':
  486.             PostMessage( hWnd, WM_SYSCOMMAND, IDC_SENSITIVITY_NORMAL + vk - '2', 0 );
  487.             break;
  488.  
  489.         case VK_DELETE:
  490.             PostMessage( hWnd, WM_SYSCOMMAND, IDC_CLEAR, 0 );
  491.             break;
  492.  
  493.         case VK_F1:
  494.             PostMessage( hWnd, WM_SYSCOMMAND, IDC_ABOUT, 0 );
  495.             break;
  496.     }
  497. }
  498.  
  499.  
  500.  
  501.  
  502. //-----------------------------------------------------------------------------
  503. // Name: OnClear()
  504. // Desc: Makes the bitmap white
  505. //-----------------------------------------------------------------------------
  506. HRESULT OnClear( HWND hWnd )
  507. {
  508.     PatBlt( g_hDC, 0, 0, SCRAWL_CXBITMAP, SCRAWL_CYBITMAP, WHITENESS );
  509.  
  510.     if( hWnd ) 
  511.         InvalidateRect( hWnd, 0, 0 );
  512.  
  513.     return S_OK;
  514. }
  515.  
  516.  
  517.  
  518.  
  519. //-----------------------------------------------------------------------------
  520. // Name: InvalidateCursorRect()
  521. // Desc: Invalidate the rectangle that contains the cursor.
  522. //       The coordinates are in client coordinates.
  523. //-----------------------------------------------------------------------------
  524. VOID InvalidateCursorRect( HWND hWnd )
  525. {
  526.     RECT rc = { g_x - g_dxCrossHot,             g_y - g_dyCrossHot,
  527.                 g_x - g_dxCrossHot + g_cxCross, g_y - g_dyCrossHot + g_cyCross };
  528.     InvalidateRect( hWnd, &rc, 0 );
  529. }
  530.  
  531.  
  532.  
  533.  
  534. //-----------------------------------------------------------------------------
  535. // Name: UpdateCursorPosition()
  536. // Desc: Move our private cursor in the requested direction, subject
  537. //       to clipping, scaling, and all that other stuff.
  538. //
  539. //       This does not redraw the cursor.  You need to do that yourself.
  540. //-----------------------------------------------------------------------------
  541. VOID UpdateCursorPosition( int dx, int dy )
  542. {   
  543.     // Pick up any leftover fuzz from last time.  This is important
  544.     // when scaling down mouse motions.  Otherwise, the user can
  545.     // drag to the right extremely slow for the length of the table
  546.     // and not get anywhere.
  547.     dx += g_dxFuzz;     
  548.     g_dxFuzz = 0;
  549.  
  550.     dy += g_dyFuzz;     
  551.     g_dyFuzz = 0;
  552.  
  553.     switch( g_iSensitivity ) 
  554.     {
  555.         case 1:     // High sensitivity: Magnify! 
  556.             dx *= 2;
  557.             dy *= 2;
  558.             break;
  559.  
  560.         case -1:    // Low sensitivity: Scale down 
  561.             g_dxFuzz = dx % 2;  // remember the fuzz for next time 
  562.             g_dyFuzz = dy % 2;
  563.             dx /= 2;
  564.             dy /= 2;
  565.             break;
  566.  
  567.         case 0:     // normal sensitivity 
  568.             // No adjustments needed 
  569.             break;
  570.     }
  571.  
  572.     g_x += dx;
  573.     g_y += dy;
  574.  
  575.     // clip the cursor to our client area
  576.     if( g_x < 0 ) 
  577.         g_x = 0;
  578.  
  579.     if( g_x >= SCRAWL_CXBITMAP )
  580.         g_x = SCRAWL_CXBITMAP - 1;
  581.  
  582.     if( g_y < 0 )  
  583.         g_y = 0;
  584.  
  585.     if( g_y >= SCRAWL_CYBITMAP ) 
  586.         g_y = SCRAWL_CYBITMAP - 1;
  587. }
  588.  
  589.  
  590.  
  591.  
  592. //-----------------------------------------------------------------------------
  593. // Name: StartPenDraw()
  594. // Desc: Called when starting pen draw.
  595. //-----------------------------------------------------------------------------
  596. VOID StartPenDraw( HWND hWnd, LEFTBUTTONINFO* plbInfo )
  597. {
  598.     // Hide the cursor while scrawling 
  599.     g_bShowCursor = FALSE;
  600.  
  601.     plbInfo->hdcWindow = GetDC( hWnd );
  602.     MoveToEx( plbInfo->hdcWindow, g_x, g_y, 0 );
  603.     MoveToEx( g_hDC, g_x, g_y, 0 );
  604.  
  605.     SelectObject( plbInfo->hdcWindow, GetStockObject(BLACK_PEN) );
  606.     SelectObject( g_hDC, GetStockObject(BLACK_PEN) );
  607.  
  608.     plbInfo->bMoved = FALSE;
  609.     plbInfo->dwSeqLastSeen = 0;
  610. }
  611.  
  612.  
  613.  
  614.  
  615. //-----------------------------------------------------------------------------
  616. // Name: FinishPenDraw()
  617. // Desc: Called when ending pen draw.
  618. //-----------------------------------------------------------------------------
  619. VOID FinishPenDraw( HANDLE hWnd )
  620. {
  621.     g_bShowCursor = TRUE;
  622. }
  623.  
  624.  
  625.  
  626.  
  627. //-----------------------------------------------------------------------------
  628. // Name: OnLeftButtonDown_FlushMotion()
  629. // Desc: Flush out any motion that we are holding.
  630. //-----------------------------------------------------------------------------
  631. VOID OnLeftButtonDown_FlushMotion( LEFTBUTTONINFO* plbInfo )
  632. {
  633.     if( plbInfo->bMoved ) 
  634.     {
  635.         plbInfo->bMoved = FALSE;
  636.         plbInfo->dwSeqLastSeen = 0;
  637.         LineTo( plbInfo->hdcWindow, g_x, g_y );
  638.         LineTo( g_hDC, g_x, g_y );
  639.     }
  640. }
  641.  
  642.  
  643.  
  644.  
  645. //-----------------------------------------------------------------------------
  646. // Name: OnRightButtonUp()
  647. // Desc: Pop up a context menu.
  648. //-----------------------------------------------------------------------------
  649. VOID OnRightButtonUp( HWND hWnd )
  650. {
  651.     // Place a popup menu where the mouse curent is
  652.     POINT pt;
  653.     pt.x = g_x;
  654.     pt.y = g_y;
  655.     ClientToScreen( hWnd, &pt );
  656.     HMENU hMenuPopup = GetSystemMenu( hWnd, FALSE );
  657.  
  658.     // Hide the cursor while moving it so you don't get annoying flicker.
  659.     ShowCursor( FALSE );
  660.     InvalidateCursorRect( hWnd );
  661.  
  662.     // Unacquire the devices so the user can interact with the menu.
  663.     g_bActive = FALSE;
  664.     SetAcquire();
  665.  
  666.     // Put the Windows cursor at the same location as our virtual cursor.
  667.     SetCursorPos( pt.x, pt.y );
  668.  
  669.     // Show the cursor now that it is moved 
  670.     ShowCursor( TRUE );
  671.     InvalidateCursorRect( hWnd );
  672.  
  673.     // Track the popup menu and return the menu item selected
  674.     UINT iMenuID = TrackPopupMenuEx( hMenuPopup, TPM_RIGHTBUTTON|TPM_RETURNCMD,
  675.                                      pt.x, pt.y, hWnd, 0 );
  676.  
  677.     if( 0 != iMenuID ) // If a menu item was selected
  678.         PostMessage( hWnd, WM_SYSCOMMAND, iMenuID, 0L );
  679. }
  680.  
  681.  
  682.  
  683.  
  684. //-----------------------------------------------------------------------------
  685. // Name: InitDirectInput()
  686. // Desc: Initialize the DirectInput variables.
  687. //-----------------------------------------------------------------------------
  688. HRESULT InitDirectInput( HWND hWnd )
  689. {
  690.     HRESULT hr;
  691.  
  692.     // Register with the DirectInput subsystem and get a pointer
  693.     // to a IDirectInput interface we can use.
  694.     // Create a DInput object
  695.     if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
  696.                                          IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
  697.         return hr;
  698.     
  699.     // Obtain an interface to the system mouse device.
  700.     if( FAILED( hr = g_pDI->CreateDevice( GUID_SysMouse, &g_pMouse, NULL ) ) )
  701.         return hr;
  702.  
  703.     // Set the data format to "mouse format" - a predefined data format 
  704.     //
  705.     // A data format specifies which controls on a device we
  706.     // are interested in, and how they should be reported.
  707.     //
  708.     // This tells DirectInput that we will be passing a
  709.     // DIMOUSESTATE structure to IDirectInputDevice::GetDeviceState.
  710.     if( FAILED( hr = g_pMouse->SetDataFormat( &c_dfDIMouse ) ) )
  711.         return hr;
  712.  
  713.     // Set the cooperativity level to let DirectInput know how
  714.     // this device should interact with the system and with other
  715.     // DirectInput applications.
  716.     if( FAILED( hr = g_pMouse->SetCooperativeLevel( hWnd, 
  717.                                          DISCL_EXCLUSIVE|DISCL_FOREGROUND ) ) )
  718.         return hr;
  719.  
  720.     // Create a win32 event which is signaled when mouse data is availible
  721.     g_hMouseEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  722.     if( NULL == g_hMouseEvent )
  723.         return E_FAIL;
  724.  
  725.     // Give the event to the mouse device
  726.     if( FAILED( hr = g_pMouse->SetEventNotification( g_hMouseEvent ) ) )
  727.         return hr;
  728.  
  729.     // Setup the buffer size for the mouse data
  730.     DIPROPDWORD dipdw;
  731.     dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  732.     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  733.     dipdw.diph.dwObj        = 0;
  734.     dipdw.diph.dwHow        = DIPH_DEVICE;
  735.     dipdw.dwData            = SAMPLE_BUFFER_SIZE; // Arbitary buffer size
  736.  
  737.     if( FAILED( hr = g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
  738.         return hr;
  739.  
  740.     // Not necessary, but nice for left handed users that have
  741.     // their swapped mouse buttons
  742.     g_bSwapMouseButtons = GetSystemMetrics( SM_SWAPBUTTON );
  743.  
  744.     return S_OK;
  745. }
  746.  
  747.  
  748.  
  749.  
  750. //-----------------------------------------------------------------------------
  751. // Name: SetAcquire()
  752. // Desc: Acquire or unacquire the mouse, depending on if the app is active
  753. //       Input device must be acquired before the GetDeviceState is called
  754. //-----------------------------------------------------------------------------
  755. HRESULT SetAcquire()
  756. {
  757.     // Nothing to do if g_pMouse is NULL
  758.     if( NULL == g_pMouse )
  759.         return S_FALSE;
  760.  
  761.     if( g_bActive ) 
  762.         g_pMouse->Acquire();
  763.     else 
  764.         g_pMouse->Unacquire();
  765.  
  766.     return S_OK;
  767. }
  768.  
  769.  
  770.  
  771.  
  772. //-----------------------------------------------------------------------------
  773. // Name: FreeDirectInput()
  774. // Desc: Initialize the DirectInput variables.
  775. //-----------------------------------------------------------------------------
  776. HRESULT FreeDirectInput()
  777. {
  778.     // Unacquire the device one last time just in case 
  779.     // the app tried to exit while the device is still acquired.
  780.     if( g_pMouse ) 
  781.         g_pMouse->Unacquire();
  782.     
  783.     // Release any DirectInput objects.
  784.     SAFE_RELEASE( g_pMouse );
  785.     SAFE_RELEASE( g_pDI );
  786.  
  787.     if( g_hMouseEvent )
  788.         CloseHandle( g_hMouseEvent );
  789.  
  790.     return S_OK;
  791. }
  792.  
  793.  
  794.  
  795.  
  796. //-----------------------------------------------------------------------------
  797. // Name: OnMouseInput()
  798. // Desc: Handles responding to any mouse input that is generated from
  799. //       the mouse event being triggered.
  800. //-----------------------------------------------------------------------------
  801. VOID OnMouseInput( HWND hWnd )
  802. {
  803.     BOOL                bDone;
  804.     DIDEVICEOBJECTDATA  od;
  805.     DWORD               dwElements;
  806.     HRESULT             hr;
  807.  
  808.     // Invalidate the old cursor so it will be erased 
  809.     InvalidateCursorRect( hWnd );
  810.  
  811.     // Attempt to read one data element.  Continue as long as
  812.     // device data is available.
  813.     bDone = FALSE;
  814.     while( !bDone ) 
  815.     {
  816.         dwElements = 1;
  817.         hr = g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), 
  818.                                       &od, &dwElements, 0 );
  819.  
  820.         if( hr == DIERR_INPUTLOST ) 
  821.         {
  822.             SetAcquire();
  823.             break;
  824.         }
  825.  
  826.         // Unable to read data or no data available
  827.         if( FAILED(hr) || dwElements == 0 ) 
  828.         {
  829.             break;
  830.         }
  831.  
  832.         // Look at the element to see what happened
  833.         switch( od.dwOfs ) 
  834.         {     
  835.             case DIMOFS_X:       // Mouse horizontal motion 
  836.                 UpdateCursorPosition( od.dwData, 0 ); 
  837.                 break;
  838.  
  839.             case DIMOFS_Y:       // Mouse vertical motion 
  840.                 UpdateCursorPosition( 0, od.dwData ); 
  841.                 break;
  842.  
  843.             case DIMOFS_BUTTON0: // Right button pressed or released 
  844.             case DIMOFS_BUTTON1: // Left button pressed or released 
  845.                 // Is the right or a swapped left button down?
  846.                 if( ( g_bSwapMouseButtons  && DIMOFS_BUTTON1 == od.dwOfs ) ||
  847.                     ( !g_bSwapMouseButtons && DIMOFS_BUTTON0 == od.dwOfs ) )
  848.                 {
  849.                     if( od.dwData & 0x80 ) 
  850.                     { 
  851.                         // left button pressed, so go into button-down mode 
  852.                         bDone = TRUE;
  853.                         OnLeftButtonDown( hWnd ); 
  854.                     }
  855.                 }
  856.  
  857.                 // is the left or a swapped right button down?
  858.                 if( ( g_bSwapMouseButtons  && DIMOFS_BUTTON0 == od.dwOfs ) ||
  859.                     ( !g_bSwapMouseButtons && DIMOFS_BUTTON1 == od.dwOfs ) )
  860.                 {
  861.                     if( !(od.dwData & 0x80) ) 
  862.                     {  
  863.                         // button released, so check context menu 
  864.                         bDone = TRUE;
  865.                         OnRightButtonUp( hWnd ); 
  866.                     }
  867.                 }
  868.                 break;
  869.         }
  870.     }
  871.  
  872.     // Invalidate the new cursor so it will be drawn 
  873.     InvalidateCursorRect( hWnd );
  874. }
  875.  
  876.  
  877.  
  878.  
  879. //-----------------------------------------------------------------------------
  880. // Name: OnLeftButtonDown()
  881. // Desc: If we are drawing a curve, then read buffered data and draw
  882. //      lines from point to point.  By reading buffered data, we can
  883. //      track the motion of the mouse accurately without coalescing.
  884. //
  885. //      This function illustrates how a non-message-based program can
  886. //      process buffered data directly from a device, processing
  887. //      messages only occasionally (as required by Windows).
  888. //
  889. //      This function also illustrates how an application can piece
  890. //      together buffered data elements based on the sequence number.
  891. //      A single mouse action (e.g., moving diagonally) is reported
  892. //      as a series of events, all with the same sequence number.
  893. //      Zero is never a valid DirectInput sequence number, so it is
  894. //      safe to use it as a sentinel value.
  895. //-----------------------------------------------------------------------------
  896. VOID OnLeftButtonDown( HWND hWnd )
  897. {
  898.     HRESULT             hr;
  899.     LEFTBUTTONINFO      lbInfo;
  900.     BOOL                bDone;
  901.     DIDEVICEOBJECTDATA  od;
  902.     DWORD               dwElements;
  903.     MSG                 msg;
  904.  
  905.     // For performance, draw directly onto the window's DC instead of
  906.     // invalidating and waiting for the WM_PAINT message.  Of course,
  907.     // we always draw onto our bitmap, too, since that's what really
  908.     // counts.
  909.  
  910.     // hide cursor and initialize button info with cursor position
  911.     StartPenDraw( hWnd, &lbInfo );
  912.     InvalidateCursorRect( hWnd );
  913.     UpdateWindow( hWnd );
  914.  
  915.     // Keep reading data elements until we see a "mouse button up" event.
  916.     bDone = FALSE;
  917.     while( !bDone ) 
  918.     {
  919.         dwElements = 1;
  920.         hr = g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), 
  921.                                       &od, &dwElements, 0 );
  922.         if( FAILED(hr) )       
  923.             break;
  924.  
  925.         // If theres no data available, finish the element 
  926.         // we have been collecting, and then process our message 
  927.         // queue so the system doesn't think the app has hung.
  928.         if( dwElements == 0 ) 
  929.         {
  930.             // if there is a partial motion, flush it out 
  931.             OnLeftButtonDown_FlushMotion( &lbInfo );
  932.  
  933.             while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 
  934.             {
  935.                 // If it's a quit message, we're outta here 
  936.                 if( msg.message == WM_QUIT ) 
  937.                 {
  938.                     // Re-post the quit message so the
  939.                     // outer loop will see it and exit.
  940.                     PostQuitMessage( (int)msg.wParam );
  941.                     bDone = TRUE;                    
  942.                     break;
  943.                 } 
  944.                 else 
  945.                 {
  946.                     TranslateMessage( &msg );
  947.                     DispatchMessage( &msg );
  948.                 }
  949.             }
  950.             continue;
  951.         }
  952.  
  953.         // If this is the start of a new event, flush out the old one 
  954.         if( od.dwSequence != lbInfo.dwSeqLastSeen ) 
  955.         {
  956.             OnLeftButtonDown_FlushMotion( &lbInfo );
  957.             lbInfo.dwSeqLastSeen = od.dwSequence;
  958.         }
  959.  
  960.         // Look at the element to see what happened 
  961.         switch( od.dwOfs ) 
  962.         {
  963.             case DIMOFS_X:      // Mouse horizontal motion 
  964.                 UpdateCursorPosition( od.dwData, 0 );
  965.                 lbInfo.bMoved = TRUE;
  966.                 break;
  967.  
  968.             case DIMOFS_Y:      // Mouse vertical motion 
  969.                 UpdateCursorPosition( 0, od.dwData );
  970.                 lbInfo.bMoved = TRUE;
  971.                 break;
  972.  
  973.             case DIMOFS_BUTTON0: // Button 0 pressed or released 
  974.             case DIMOFS_BUTTON1: // Button 1 pressed or released 
  975.                 if( ( g_bSwapMouseButtons  && DIMOFS_BUTTON1 == od.dwOfs ) ||
  976.                     ( !g_bSwapMouseButtons && DIMOFS_BUTTON0 == od.dwOfs ) )
  977.                 {
  978.                     if( !(od.dwData & 0x80) ) 
  979.                     { 
  980.                         // Button released, so flush out dregs 
  981.                         bDone = TRUE;
  982.                         OnLeftButtonDown_FlushMotion( &lbInfo ); 
  983.                     }
  984.                 }
  985.                 break;
  986.         }
  987.     }
  988.  
  989.     ReleaseDC( hWnd, lbInfo.hdcWindow );
  990.  
  991.     // Re-show the cursor now that scrawling is finished 
  992.     FinishPenDraw( hWnd );
  993.     InvalidateCursorRect( hWnd );
  994. }
  995.  
  996.  
  997.  
  998.